ローカル環境で開発した機械学習モデルの Docker コンテナを構築し、ECRへ登録してみた

ローカル環境で開発した機械学習モデルの Docker コンテナを構築し、ECRへ登録してみた

Clock Icon2024.09.19

こんにちは!よしななです。
今回は、ローカルで開発した機械学習モデルを Docker コンテナ化し、Amazon ECR に登録し、登録したイメージをプルするところまでを検証したので、備忘録としてブログに残します。

目次

  • 前提
  • 事前準備
    • 機械学習モデルの用意
    • ディレクトリの準備
  • Dockerfile の作成
  • ECRに登録してみる
    • ECR プライベートリポジトリ作成
    • Docker イメージのビルド
    • ECR へ Push する
  • 最後に

前提

以下の環境で実行しています。

'''ts

事前準備

機械学習モデルの用意

ECR にプッシュする機械学習モデルを用意します。
今回は、以前に構築した自分の飼い猫かそれ以外の猫を判定する画像分類モデルを使用します。

https://devio2024-front-preview.developers.io/articles/tensorflow/

上記の画像分類モデルを.pyファイルに一つにまとめます。

# ライブラリインポート
import numpy as np
import tensorflow as tf

# ディレクトリパスの指定
data_dir = "/app/data"

# バッチサイズの指定
batch_size = 32

# データサイズの指定
img_height = 180
img_width = 180

# データの分割
## 学習用
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split = 0.3,
    subset = "training",
    seed = 1,
    image_size=(img_height, img_width),
    batch_size=batch_size
)

## 検証用
val_ds = tf.keras.utils.image_dataset_from_directory(
    data_dir,
    validation_split=0.3,
    subset="validation",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size
)

# 正規化
normalization_layer = tf.keras.layers.Rescaling(1./255)

normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
print(np.min(first_image), np.max(first_image))

# データセットの最適化
AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

# CNNの構成
num_classes = 2

model = tf.keras.Sequential([
    tf.keras.layers.Rescaling(1./255),
    tf.keras.layers.Conv2D(32, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(128, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(num_classes),
    tf.keras.layers.Softmax()
])

# コンパイル
model.compile(
  optimizer=tf.keras.optimizers.Adam(),
  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
  metrics=['accuracy'])

# トレーニング
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=10,
  verbose=1
)

ディレクトリの準備

次に、画像分類モデル、Dockerfile と Image_Classification_Model.py、dataset.zip を構築環境を格納するためのディレクトリを作成します。ディレクトリ構成としては以下となります。
Dockerfile 上で 学習用データセット:dataset.zip の解凍を行い、dataフォルダに格納します。

2024-09-17_15h35_00

Dockerfile の作成

事前準備が完了したら、Dockerfile を作成します。
Docker は Dockerfile から命令を読み込み、自動的にイメージをビルドできます。
Dockerfile はテキストファイルであり、イメージを作り上げるために実行するコマンドライン命令を、すべてこのファイルに含めることができます。
作成した Dockerfile を元に、docker buildを実行すると、Dockerfile に記述したコマンドラインの処理を行い、ビルド結果となるイメージが作成されます。

参考ドキュメント:
https://docs.docker.jp/engine/reference/builder.html#
https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html

今回作成する Dockerfile に必要な要素としては以下の通りとなります。

  1. FROM
    • ベースとなるイメージを指定します。Dockerfile の先頭に必ず必要です。
    • 今回は、TensorFlow のコンテナイメージを使用します。
    • タグを省略すると:latestが指定されたとみなします。
  2. LABEL
    • イメージに、ベンダ名、作者名、バージョン情報などのラベル情報(メタデータ)を設定します。
    • 今回は、バージョン情報と Docker イメージを作成した作者名を記入します。
    • LABELを呼び出すたびにイメージのレイヤが増えるため、ひとつにまとめて設定することが推奨されています。
  3. WORKDIR
    • 作業ディレクトリを指定します。
  4. RUN
    • ビルド時に実行するコマンドを指定します。
    • 今回は、スクリプト実行時に必要となるnumpyライブラリをインストールし、COPYコマンドでコピーしたデータセットを解凍し、zip ファイルを削除するコマンドを実行します。
    • LABELと同じくRUNコマンドを実行するたびイメージのレイヤが増え Docker のビルドが遅くなるため、コマンドを&&;で連結し、複数のコマンドをひとつのRUNにまとめて記述することが推奨されています。
  5. COPY
    • ホストからコンテナイメージにファイルやディレクトリをコピーします。
  6. CMD
    • docker run時に実行するコマンドを指定します。

上記を組み合わせて、以下の通り Dockerfile を作成します。
こちらの Dockerfile を元に、docker buildコマンドを実行していきます。

# 公式イメージを使用する
FROM tensorflow/tensorflow:latest-gpu

# コンテナの作者名とバージョンを指定
LABEL mainteiner="yoshidanana <メールアドレス入力>" \
      version="1.0"

# 作業ディレクトリを指定
WORKDIR /app

# 必要なパッケージをインストール
RUN pip install numpy==1.24.3

# ソースコードとデータセットをコピー
COPY Image_Classification_Model.py /app/
COPY dataset.zip /app/

# データセットを解凍し、zipデータを削除
RUN unzip dataset.zip -d /app/data && rm dataset.zip

# 機械学習モデル.py の実行
CMD ["python", "Image_Classification_Model.py"]

ECRに登録してみる

それでは、作成した Dockerfile を使用して機械学習モデルを ECR に登録してみます。

ECR プライベートリポジトリ作成

プライベートリポジトリは AWS マネージドコンソール上からも作成できますが、今回は AWS CLI 上から作成します。
--repository-nameで作成するプライベートリポジトリ名を指定します。
以下のコマンドを実行します。実施前に ECR の操作権限を持っているロールの Assume role を行っています。

$ aws ecr create-repository --repository-name test

実行に成功すると、以下の値が返ります。

    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:"<アカウントID>":repository/test",
        "registryId": ""<アカウントID>"",
        "repositoryName": "test",
        "repositoryUri": ""<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test"
    } ...

作成したプライベートリポジトリを確認したい場合は、以下のコマンドを実行します。

$ aws ecr describe-repositories --repository-name  test

以下の通り、作成したプライベートリポジトリの設定値が返ります。

{
"repositories": [
        {
            "repositoryArn": "arn:aws:ecr:ap-northeast-1:"<アカウントID>":repository/test",
            "registryId": ""<アカウントID>"",
            "repositoryName": "test",
            "repositoryUri": ""<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test",
            "createdAt": "2024-09-17T16:17:03.293000+09:00",
            "imageTagMutability": "MUTABLE",
            "imageScanningConfiguration": {
                "scanOnPush": false
            },
            "encryptionConfiguration": {
                "encryptionType": "AES256"
            }
        }
    ]
}

Docker イメージのビルド

次に、作成した Dockerfile を元にイメージをビルドします。
docker ターミナルを開き、Dockerfile が配置されているディレクトリに移動します。

cd app

次に、以下のコマンドで Dockerfile をビルドします。

docker build . -t classification-model-image:latest

-tオプションでリポジトリ名とタグを指定しています。

-t <リポジトリ名>:<タグ>

〇ビルド実行中のターミナル:

2024-09-17_18h03_21

ビルドが完了したら、以下のコマンドで作成したイメージを確認します。
Docker イメージがビルドされていることを確認しました。

docker image ls

REPOSITORY                                          TAG                    IMAGE ID       CREATED         SIZE
classification-model-image                          latest                 fe06c4e2621a   2 minutes ago   7.61GB

ECR へ Pushする

前項で、プライベートリポジトリの作成と Docker イメージのビルドを実行しました。
次に、ビルドした Docker イメージをプライベートリポジトリに Push していきます。

Push に必要なコマンドは、以下の通りマネージドコンソールからも確認が可能です。

2024-09-19_09h55_40

1.Docker クライアントの認証

Docker コンテナを Push するためには、Amazon ECR レポジトリに対し、Docker クライアントを認証する必要があります。
以下のコマンドを実行します。

$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin "<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com

Login Succeededが返れば認証が成功です。

2.Dockerfile を 元に Docker イメージをビルド

2.のコマンドでは Docker イメージの作成となりますが、すでに作成済みですので飛ばします。

3.イメージへタグ付け

次に、Docker イメージのビルドの際に付与したタグを ECR プライベートリポジトリ名に変更します。

$ docker tag classification-model-image:latest "<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test:latest

コマンドの引数の内容は$ docker tag "<元のリポジトリ名>":"<元のタグ名>" "<変更後のリポジトリ名>":"<変更後のタグ名>"となります。 実行後に以下のコマンドでタグが変更されたかイメージを確認します。

docker image ls

コマンドが変更されていることを確認しました。

REPOSITORY                                                                         TAG                    IMAGE ID       CREATED          SIZE
"<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test   latest                 fe06c4e2621a   13 minutes ago   7.61GB 

4.ECR へ AWS CLI で Push

タグの変更が完了したら、Docker イメージを ローカル環境から ECR に Push していきます。
以下のコマンドを実行します。

$ docker push "<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test:latest

〇実行中の様子:

2024-09-17_18h32_44

Push が完了したら、以下のコマンドで Docker イメージを確認します。
ECR 上に Docker イメージが Push されていることを確認しました。

aws ecr describe-images --repository-name test
{
    "imageDetails": [
        {
            "registryId": "<アカウントID>",
            "repositoryName": "test",
            "imageDigest": "sha256:08de9457b3988619401bc955451babcfcbc349beb62fb5cbbf8826f8616f4fe9",
            "imageTags": [
                "latest"
            ],
            "imageSizeInBytes": 3983229617,
            "imagePushedAt": "2024-09-17T18:37:33+09:00",
            "imageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "artifactMediaType": "application/vnd.docker.container.image.v1+json"
        }
    ]
}

無事にプッシュできていることが確認できました。
マネージドコンソール上からも以下の通りプッシュできていることを確認しました。

2024-09-17_18h46_29

最後に

今回、Docker イメージに Push してみて、Dockerfile の書き方のベストプラクティスを知ることができてよかったです。ビルド時間に影響があることなど普段意識してこなかったのですが、だいぶ短縮できることを知りました。
ここまで読んでいただき、ありがとうございました!

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.